
#ifndef _B5M_COMMON_GUARD_H_
#define _B5M_COMMON_GUARD_H_

#include "common.h"

B5M_BEGIN_VERSIONED_NAMESPACE_DECL
	
	/**
 * @class B5M_Guard
 *
 * @brief This data structure is meant to be used within a method or
 * function...  It performs automatic acquisition and release of
 * a parameterized synchronization object B5M_LOCK.
 *
 * The B5M_LOCK class given as an actual parameter must provide, at
 * the very least the acquire(), tryacquire(), release(), and
 * remove() methods.
 *
 * @warning A successfully constructed B5M_Guard does NOT mean that the
 * lock was acquired!  It is the caller's responsibility, after
 * constructing an B5M_Guard, to check whether the lock was successfully
 * acquired.  Code like this is dangerous:
 *   {
 *     B5M_Guard<B5M_Lock> g(lock);
 *     ... perform critical operation requiring lock to be held ...
 *   }
 * Instead, one must do something like this:
 *   {
 *     B5M_Guard<B5M_Lock> g(lock);
 *     if (! g.locked())
 *       {
 *         ... handle error ...
 *       }
 *     else
 *       {
 *         ... perform critical operation requiring lock to be held ...
 *       }
 *   }
 * The B5M_GUARD_RETURN() and B5M_GUARD_REACTION() macros are designed to
 * to help with this.
 */
template <class B5M_LOCK>
class B5M_Guard
{
public:

  // = Initialization and termination methods.
  B5M_Guard (B5M_LOCK &l)
  : lock_ (&l),
	owner_ (0)
  {
	  this->acquire ();
  }

  /// Implicitly and automatically acquire (or try to acquire) the
  /// lock.  If @a block is non-0 then acquire() the B5M_LOCK, else
  /// tryacquire() it.
  B5M_Guard (B5M_LOCK &l, bool block)
  : lock_ (&l),
	owner_ (0)
  {
	  if (block)
		  this->acquire ();
	  else
		  this->tryacquire ();
  }

  /// Initialize the guard without implicitly acquiring the lock. The
  /// @a become_owner parameter indicates whether the guard should release
  /// the lock implicitly on destruction. The @a block parameter is
  /// ignored and is used here to disambiguate with the preceding
  /// constructor.
  B5M_Guard (B5M_LOCK &l, bool block, int become_owner)
  : lock_ (&l),
	owner_ (become_owner == 0 ? -1 : 0)
  {

  }

  /// Implicitly release the lock.
  ~B5M_Guard (void)
  {
	  this->release ();
  }

  // = Lock accessors.

  /// Explicitly acquire the lock.
  int acquire (void)
  {
	  return this->owner_ = this->lock_->acquire ();
  }

  /// Conditionally acquire the lock (i.e., won't block).
  int tryacquire (void)
  {
	  return this->owner_ = this->lock_->tryacquire ();
  }

  /// Explicitly release the lock, but only if it is held!
  int release (void)
  {
	  if (this->owner_ == -1)
		  return -1;
	  else
	  {
		  this->owner_ = -1;
		  return this->lock_->release ();
	  }
  }

  /// Relinquish ownership of the lock so that it is not released
  /// implicitly in the destructor.
  void disown (void)
  {
	  this->owner_ = -1;
  }

  // = Utility methods.
  /// true if locked, false if couldn't acquire the lock
  /// (errno will contain the reason for this).
  bool locked (void) const
  {
	  return this->owner_ != -1;
  }

  /// Explicitly remove the lock.
  int remove (void)
  {
	  return this->lock_->remove ();
  }
protected:

  /// Helper, meant for subclass only.
  B5M_Guard (B5M_LOCK *lock): lock_ (lock), owner_ (0) {}

  /// Pointer to the B5M_LOCK we're guarding.
  B5M_LOCK *lock_;

  /// Keeps track of whether we acquired the lock or failed.
  int owner_;

private:
  // = Prevent assignment and initialization.
  void operator= (const B5M_Guard<B5M_LOCK> &);
  B5M_Guard (const B5M_Guard<B5M_LOCK> &);
};

B5M_END_VERSIONED_NAMESPACE_DECL

#endif